/// Qualab - 24/11/2011

#include <wink/window.h>
#include <wink/app.h>
#include <wink/errors.h>
#include <internal/windowdata.h>

namespace wink
{
    Window::Window( void* at )
        : mData(), mBlock()
    {
        Reset( at );
    }

    Window::Window( PString text, void* at )
        : mData(), mBlock()
    {
        Create( text, at );
    }

    Window::Window( PString text, int width, int height, void* at )
        : mData(), mBlock()
    {
        Create( text, width, height, at );
    }

    Window::Window( PString text, int x, int y, int width, int height, void* at )
        : mData(), mBlock()
    {
        Create( text, width, height, x, y, at );
    }

    Window::Window( PString text, int x, int y, int width, int height, int show_mode, void* at )
        : mData(), mBlock()
    {
        Create( text, width, height, x, y, show_mode, at );
    }

    Window::Window( const Window& source, bool clone, void* at )
        : mData(), mBlock()
    {
        Reset( source, clone, at );
    }

    Window::Window( HWND handle, void* at )
        : mData(), mBlock()
    {
        Reset( handle, at );
    }

    Window::~Window()
    {
        Close();
    }

    bool Window::IsSet() const
    {
        return mData != NULL;
    }

    bool Window::IsNotSet() const
    {
        return !IsSet();
    }

    void Window::AssertIsSet() const
    {
        if( IsNotSet() )
            throw ErrorWindowDataNotSet();
    }

    bool Window::IsOwnData() const
    {
        return !mBlock.empty();
    }

    Window& Window::operator = ( const Window& source )
    {
        Close();
        if( !source.IsOwnData() )
        {
            mData = source.mData;
        }
        else
            throw ErrorWindowDataNoncopiable();
        return *this;
    }

    void Window::Create( PString text, void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData( text );
        App::Register( *this );
    }

    void Window::Create( LPCTSTR text, int width, int height, void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData( text, width, height );
        App::Register( *this );
    }

    void Window::Create( LPCTSTR text, int x, int y, int width, int height, void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData( text, width, height, x, y );
        App::Register( *this );
    }

    void Window::Create( LPCTSTR text, int x, int y, int width, int height, int show_mode, void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData( text, width, height, x, y, show_mode );
        App::Register( *this );
    }

    Window::operator HWND () const
    {
        return Handle();
    }

    HWND Window::Handle() const
    {
        AssertIsSet();
        return mData->Handle();
    }

    int Window::Left() const
    {
        AssertIsSet();
        return mData->Left();
    }

    int Window::Top() const
    {
        AssertIsSet();
        return mData->Top();
    }

    int Window::Right() const
    {
        AssertIsSet();
        return mData->Right();
    }

    int Window::Bottom() const
    {
        AssertIsSet();
        return mData->Bottom();
    }

    int Window::Width() const
    {
        AssertIsSet();
        return mData->Width();
    }

    int Window::Height() const
    {
        AssertIsSet();
        return mData->Height();
    }

    int Window::ClientWidth() const
    {
        AssertIsSet();
        return mData->ClientWidth();
    }

    int Window::ClientHeight() const
    {
        AssertIsSet();
        return mData->ClientHeight();
    }

    void Window::UpdateRect()
    {
        AssertIsSet();
        mData->UpdateRect();
    }

    const Rect& Window::Shape() const
    {
        AssertIsSet();
        return mData->Shape();
    }

    const Rect& Window::Client() const
    {
        AssertIsSet();
        return mData->Client();
    }

    void Window::Close()
    {
        if( IsOwnData() )
        {
            App::Erase( *this );
            mData->Close();
            mData = NULL;
            mBlock.clear();
        }
    }

    void Window::Reset( void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData();
    }

    void Window::Reset( HWND handle, void* at )
    {
        Close();
        if( !at )
        {
            mBlock.resize( sizeof( WindowData ) );
            at = &mBlock[0];
        }
        mData = new(at) WindowData( handle );
    }

    void Window::Reset( const Window& source, bool clone, void* at )
    {
        Close();
        if( !clone )
        {
            if( !source.IsOwnData() )
            {
                mData = source.mData;
            }
            else
            {
                throw ErrorWindowDataNoncopiable();
            }
        }
        else
        {
            if( !at )
            {
                mBlock.resize( sizeof( WindowData ) );
                at = &mBlock[0];
            }
            mData = new(at) WindowData( *source.mData );
        }
    }

    void Window::SetOnMove( OnMoveProc proc )
    {
        AssertIsSet();
        mData->SetOnMove( proc );
    }

    void Window::SetOnResize( OnResizeProc proc )
    {
        AssertIsSet();
        mData->SetOnResize( proc );
    }

    void Window::SetOnMouseLDown( Window::OnMouseLDownProc proc )
    {
        AssertIsSet();
        mData->SetOnMouseLDown( proc );
    }

    void Window::SetOnMouseLUp( Window::OnMouseLUpProc proc )
    {
        AssertIsSet();
        mData->SetOnMouseLUp( proc );
    }

    void Window::SetOnClose( OnCloseProc proc )
    {
        AssertIsSet();
        mData->SetOnClose( proc );
    }

    int Window::OnMove()
    {
        AssertIsSet();
        return mData->OnMove( *this );
    }

    int Window::OnResize()
    {
        AssertIsSet();
        return mData->OnResize( *this );
    }

    int Window::OnMouseLDown( int x, int y )
    {
        AssertIsSet();
        return mData->OnMouseLDown( *this, x, y );
    }

    int Window::OnMouseLUp( int x, int y )
    {
        AssertIsSet();
        return mData->OnMouseLUp( *this, x, y );
    }

    void Window::OnClose()
    {
        AssertIsSet();
        mData->OnClose( *this );
    }
}
